iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
Build on Google AI

Build on Google AI :長者照護 —— 口腔機能訓練 與 延緩認知退化系列 第 7 篇

臉部偵測和臉部網格偵測不同嗎? Face Detection vs. Face Mesh Detection

  • 分享至 

  • xImage
  •  

一、前言

昨天我們已經確立了健口動一動 App 功能的開發清單與實作方向。

其中,
健口操的嘴型動作是否正確,會由 MediaPipe Face Landmarker 提供臉部關鍵點與表情特徵,再由 App 根據嘴巴開合比例、寬度及動作規則進行判斷。

臉部網格偵測究竟是什麼? 是偵測人臉在哪裡嗎?

讓我們往下看吧!


二、臉部偵測和臉部網格偵測不同嗎?

比較項目 臉部偵測(Face Detection) 臉部網格偵測(Face Mesh Detection)
核心問題 畫面中有沒有臉?臉在哪裡? 這張臉的形狀與表情長什麼樣子?
主要輸出 邊界框、信心分數、少量關鍵點 478 個正規化臉部關鍵點,包含 x、y 與相對深度 z。
三角網格 通常沒有 將相鄰的 3D 關鍵點依固定拓撲連接成三角面,形成臉部網格(Face Mesh)
資訊密度 低 高
運算成本 低 較高
典型用途 人臉計數、自動對焦、裁切人臉、隱私打碼、人臉辨識的前處理 AR 特效、Avatar 驅動、臉部貼圖、表情與動作判斷

從健口動一動App的開發來看,臉部偵測(Face Detection)確認長輩有沒有對準鏡頭、臉部網格偵測(Face Mesh Detection)確認嘴部、臉部的動作。另外,兩者都不會進行身分識別:它們只判斷「有沒有臉」與「臉怎麼動」,不會辨認「這是誰」。


三、Face Landmarker 偵測流程

剛剛介紹的臉部偵測和臉部網格偵測,其實是上下游的關係。
以 MediaPipe Face Landmarker 為例,實際流程是:

實務上,有一點發現:
健口動一動的專案裡只有建立一個 FaceLandmarker,沒有另外呼叫 FaceDetector
但圖中的階段 1 確實存在,只是被完整封裝在 Face Landmarker Task 內部,並在需要定位或重新定位人臉時執行。


四、實作:繪製臉部關鍵點

  1. 原生 iOS 挑出臉部關鍵點(模型吐478個,只取健口操需要的)
static var contourIndices: [Int] {
        outerLip + innerLip + faceEdges + upperCheekA + upperCheekB + lowerCheekA + lowerCheekB + anchors + ovalA + ovalB
}
  1. 轉浮點數
var contour = [Float]()
contour.reserveCapacity(Self.contourIndices.count * 2)

for index in Self.contourIndices {
    contour.append(Float(landmarks[index].x))
    contour.append(Float(landmarks[index].y))
}
  1. 橋接
    
extension FaceMeshPlugin: FaceLandmarkerServiceDelegate {

    func faceLandmarkerService(_ service: FaceLandmarkerService, didProduce frame: FaceFrame?) {
        DispatchQueue.main.async {
            guard let sink = self.eventSink else { return }

            guard let frame = frame else {
                sink(["hasFace": false])
                return
            }

            // 走 Float32List 而不是 List<Double>。
            // 84 個浮點數如果包成 Dart List,每秒 30 次要配置 84 個 NSNumber;
            // typed data 是整塊記憶體直接過去,差距在持續串流下很明顯。
            let contourData = frame.contour.withUnsafeBufferPointer { Data(buffer: $0) }

            sink([
                "hasFace": true,
                "blendshapes": frame.blendshapes,
                "yaw": frame.yaw,
                "rollDegrees": frame.rollDegrees,
                "mouthOpenRatio": frame.mouthOpenRatio,
                "mouthWidthRatio": frame.mouthWidthRatio,
                "contour": FlutterStandardTypedData(float32: contourData),
                "imageWidth": frame.imageWidth,
                "imageHeight": frame.imageHeight
            ])
        }
    }
}
        
        

4.Dart 接住

factory FaceFrame.fromMap(Map<Object?, Object?> map) {
    if (map['hasFace'] != true) return const FaceFrame(hasFace: false);

    final raw = (map['blendshapes'] as Map?) ?? const {};
    return FaceFrame(
      hasFace: true,
      blendshapes: {
        for (final entry in raw.entries)
          entry.key as String: (entry.value as num).toDouble(),
      },
      yaw: (map['yaw'] as num?)?.toDouble() ?? 0,
      rollDegrees: (map['rollDegrees'] as num?)?.toDouble() ?? 0,
      mouthOpenRatio: (map['mouthOpenRatio'] as num?)?.toDouble() ?? 0,
      mouthWidthRatio: (map['mouthWidthRatio'] as num?)?.toDouble() ?? 0,
      contour: map['contour'] as Float32List?,
      imageSize: Size(
        ((map['imageWidth'] as num?) ?? 480).toDouble(),
        ((map['imageHeight'] as num?) ?? 640).toDouble(),
      ),
    );
  }

5.繪製

void _paintPoints(Canvas canvas, FaceMeshProjection projection) {
    
    final halo = Paint()..color = Colors.black.withValues(alpha: 0.45);

    for (final group in kLandmarkGroups) {
      final dot = Paint()..color = group.color;
      for (final point in group.pointsOf(frame)) {
        final at = projection.project(point);
        canvas.drawCircle(at, 4.0, halo);
        canvas.drawCircle(at, 2.4, dot);
      }
    }
  }

怎麼知道要畫在哪?
靠三個數字:影格的真實像素(原生端給)、painter 在螢幕上的大小(Flutter 給)、點的 0~1 相對位置(MediaPipe 給)。MediaPipe 不知道使用者螢幕多大,只能給相對位置,所以這段換算得自己補——三個數字到齊就算得出來。


五、小結

今天我們一起認識了「Face Detection vs. Face Mesh Detection」
知道臉部偵測和臉部網格偵測的差異及上下游關係,
實作繪製臉部關鍵點,看了從原生iOS橋接到Flutter的概略流程。


六、預告

明天就讓我們進一步認識 Google MediaPipe Face Landmarker !

感謝有緣看到這的你~
希望佛菩薩也祝福你:🌟平安健康 開心幸福🌟
南無觀世音菩薩🍀 南無地藏菩薩🏠 南無阿彌陀佛☀️


上一篇
健口動一動 App:功能清單
下一篇
少年,我看你面相不錯。你聽過 Google MediaPipe Face Landmarker 嗎?(上)
系列文
Build on Google AI :長者照護 —— 口腔機能訓練 與 延緩認知退化 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言